package mint.modules.generator;

import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;

import org.antlr.runtime.tree.Tree;

import mint.modules.generator.expression.ExprNode;
import mint.modules.generator.expression.Expression;
import mint.modules.generator.expression.ExpressionType;
import mint.modules.symbol.FieldSymbol;
import mint.modules.symbol.Symbol;
import mint.modules.symbol.SymbolType;
import mint.modules.type.Type;
import mint.modules.type.TypeId;

public class FieldDeclarationNode implements MintNode {
	private String name;
	private String modifier;
	private Type type;
	private Scope parent;
	private List<String> errors;
	private int line;
	private Expression exprNode;	
	
	public FieldDeclarationNode(Scope theParent, Tree fieldTreeNode, String theModifier, Type theType)
	{
		name = fieldTreeNode.getText();
		modifier = theModifier;
		type = theType;
		parent = theParent;
		errors = new ArrayList<String>();
		line = fieldTreeNode.getLine();
		if (fieldTreeNode.getChildCount() > 0)
			exprNode = new ExprNode(parent, ExpressionType.Right, fieldTreeNode.getChild(0));
		else
			exprNode = null;
	}
	
	public String getName()
	{
		return name;
	}
	
	public String getModifier()
	{
		return modifier;
	}
	
	public Type getType()
	{
		return type;
	}
	
	public void emitInitializer(Formatter output)
	{	
		FieldSymbol thisSymbol = (FieldSymbol)parent.searchSymbol("this", parent.getContainerClass(), false);
		String leftEmit = "";
		
		output.format("aload_0\n");
		if (exprNode != null)
		{
			exprNode.emit(output);
			if (type.equals(Type.floatType) && exprNode.equals(Type.intType))
				output.format("i2f\n");
			
			leftEmit = String.format("putfield %s/%s %s", thisSymbol.getType().emitTypeName(), name, type.emit());
			
			output.format("%s\n", leftEmit);
		} else
		{
			if (type.equals(Type.intType) || type.equals(Type.booleanType))
				output.format("iconst_0\n");
			else if (type.equals(Type.floatType))
				output.format("fconst_0\n");
			else
				output.format("aconst_null\n");
			leftEmit = String.format("putfield %s/%s %s", thisSymbol.getType().emitTypeName(), name, type.emit());
			output.format("%s\n", leftEmit);
		}
	}

	@Override
	public void testSemantic() {
		if (type.getTypeId() == TypeId.Object || type.getTypeId() == TypeId.ObjectArray)
		{
			Symbol symbol = parent.searchSymbol(type.getName(), parent.getContainerClass(), false);
			
			if (symbol == null)
				errors.add(line + ": " + type.getName() + " doesn't exists on this scope");
			else
				if (symbol.getSymbolType() != SymbolType.Class)
					errors.add(line + ": " + type.getName() + " isn't a type name");
		}
		
		if(exprNode != null)
		{
			exprNode.testSemantic();			
			
			if (type.equals(Type.floatType))
				if (!(exprNode.getType().equals(Type.intType) || exprNode.getType().equals(Type.floatType)))
					errors.add(line + ": incompatible types: " + type.getCompleteName() + " and " + exprNode.getType().getCompleteName());
			else
				if (!type.equals(exprNode.getType()))
				{
					if(!exprNode.getType().equals(Type.objectNullType))
						errors.add(line + ": incompatible types: " + type.getCompleteName() + " and " + exprNode.getType().getCompleteName());
				}
		}		
	}

	@Override
	public List<String> getSemanticErrors() {
		return errors;
	}

	@Override
	public void emit(Formatter output) {
		output.format(";Field %s\n", name);
		output.format(".field %s %s %s\n", modifier, name, type.emit());
	}

	@Override
	public int getLine() {
		return line;
	}

}
